#ifndef __RING_QUEUE__
#define __RING_QUEUE__

#include <iostream>
#include <vector>
#include <pthread.h>
#include <sys/sem.h>
#include <semaphore.h>


constexpr int defaultcapacity_ = 10;

template<class T>
class RingQueue
{

    void P(sem_t *sem)
    {
        sem_wait(sem);
    }
    
    void V(sem_t *sem)
    {
        sem_post(sem);
    }
    
    void Lock(pthread_mutex_t *mutex)
    {
        pthread_mutex_lock(mutex);
    }

    void Unlock(pthread_mutex_t *mutex)
    {
        pthread_mutex_unlock(mutex);
    }

public:
    RingQueue(const int& capacity = defaultcapacity_)
    :ringqueue_(capacity) , capacity_(capacity)
    {
        pthread_mutex_init(&pmutex_ , nullptr);
        pthread_mutex_init(&cmutex_ , nullptr);
        sem_init(&cdata_sem_ , 0 , 0);
        sem_init(&pdata_sem_ , 0 , capacity_);
    }

    ~RingQueue()
    {
        pthread_mutex_destroy(&pmutex_);
        pthread_mutex_destroy(&cmutex_);
        sem_destroy(&pdata_sem_);
        sem_destroy(&cdata_sem_);
    }

    void push(const T &data)
    {
        // 1. 先申请信号量
        // 2. 申请到信号让才能访问临界资源,并且生产者不能同时放入数据
        // 3. 放入数据
        // 4. 解锁,将消费者的信号量增加
        P(&pdata_sem_);
        Lock(&pmutex_);
        ringqueue_[pstep_++] = data;
        pstep_ %= capacity_;
        Unlock(&pmutex_);
        V(&cdata_sem_);
    }

    T pop()
    {
        // 1. 申请信号量
        // 2. 访问临界资源,保证生产者不能同时拿数据
        // 3. 拿数据
        // 4. 解锁,将生产者的信号量增加
        P(&cdata_sem_);
        Lock(&cmutex_);

        T ret = ringqueue_[cstep_++];
        cstep_ %= capacity_;

        Unlock(&cmutex_);
        V(&pdata_sem_);
        return ret;
    }

private:
    std::vector<T> ringqueue_;
    sem_t cdata_sem_;
    sem_t pdata_sem_;
    pthread_mutex_t pmutex_;
    pthread_mutex_t cmutex_;
    int capacity_;
    int cstep_ = 0;
    int pstep_ = 0;
};



#endif